package com.springboot.demo.design.pattern.idempotent;

import com.springboot.common.entity.request.ParameterBody;
import com.springboot.common.entity.response.Result;
import com.springboot.common.lock.Lock;
import com.springboot.common.lock.LockFactory;
import com.springboot.demo.design.pattern.form.Form;
import com.springboot.demo.util.redis.RedisUtil;
import java.util.concurrent.TimeUnit;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 幂等工具实现类
 *
 * @author supu
 * @date 2019-03-26 11:11
 **/
@Log4j2
@Component
public class StandardIdempotentHelper implements IdempotentHelper {
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private RedisUtil redisUtil;

    private static final ThreadLocal<Lock> LOCK_THREAD_LOCAL = new ThreadLocal<>();

    @Override
    public <U, V extends ParameterBody> boolean compareAndSet(Form<U, V> form) throws Exception{
        Result<U> result = form.getResult();
        if (result != null && result.isSuccess()) {
            // 将成功的结果放入缓存
            redisUtil.setValue(getCachePath(form), result, 60 * 60);
        }

        Lock lock = LOCK_THREAD_LOCAL.get();
        if (lock != null) {
            lock.unLock();
            LOCK_THREAD_LOCAL.remove();
        }

        return true;
    }

    @Override
    public <U, V extends ParameterBody> boolean compareAndGet(Form<U, V> form) throws Exception{
        Lock lock = lockFactory.getLock(getLockPath(form));
        LOCK_THREAD_LOCAL.set(lock);

        boolean isSuccess = lock.tryLock(10, TimeUnit.SECONDS);
        if (!isSuccess) {
            throw new Exception("获取锁异常");
        }

        Result result = (Result) redisUtil.getValue(getCachePath(form));
        if (result != null && result.isSuccess()) {
            // 将成功的结果返回表单
            form.setResult(result);
            return true;
        }

        return false;
    }

    private <U, V extends ParameterBody> String getLockPath(Form<U, V> form) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(form.getFormEnum().getCode()).append("/").append(form.getFormNo());
        return stringBuffer.toString();
    }

    private <U, V extends ParameterBody> String getCachePath(Form<U, V> form) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(form.getFormEnum().getCode()).append("_").append(form.getFormNo());
        return stringBuffer.toString();
    }
}
